﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Framework.Map;

namespace Framework.Physics.AI
{
    class AStar
    {
        public List<Tile> lstOpen; // Liste mit Knoten die Untersuchtwerden müssen 
        public List<Tile> lstClosed; // Liste mit Knoten die untersucht wurden 

        private Tile tStartTile; // StartTile = Startpunkt
        private Tile tEndTile; // EntTile = Zielpunkt


        public AStar()
        {
            lstClosed = new List<Tile>();
            lstOpen = new List<Tile>();
        }


        public int GetH(Tile From, Tile To, TileMap Map) //H berechnen
        {
            int x = 0, y = 0;

            if (From.TileX > To.TileX)
            {
                x = From.TileX - To.TileX;
            }
            else
            {
                x = To.TileX - From.TileX;
            }

            if (From.TileY > To.TileY)
            {
                y = From.TileY - To.TileY;
            }
            else
            {
                y = To.TileY - From.TileY;
            }

            int Temp = x + y;

            return Temp * 10;
        }


        public List<Tile> FindPath(Tile StartTile, Tile EndTile, TileMap TheMap)
        {
            tStartTile = StartTile; // StartTile und EntTile setzen 
            tEndTile = EndTile;

            StartTile.GCount = 0; // G Wert - Kosten auf 0 setzen 
            StartTile.HCount = 0; // G Wert - Kosten auf 0 setzen 
            StartTile.FCount = StartTile.GCount + StartTile.HCount; //F wert Kosten auf 0 setzen 
            lstOpen.Add(StartTile); // Startknoten zu der "Untersuchwerdenliste" hinzufügen

            while (lstOpen.Count > 0)
            {
                if (lstClosed.Contains(EndTile)) // Wenn der Endknoten gefunden wurde 
                {
                    List<Tile> ReturnList = new List<Tile>(); // Neue Liste 
                    Tile TempReturnNode = lstClosed[lstClosed.Count - 1]; // Den Endknoten nehmen ... 
                    while (TempReturnNode != StartTile) //  ... Und solang zurückverfolgen bis der Start gefunden wurde 
                    {
                        ReturnList.Add(TempReturnNode);
                        TempReturnNode = TempReturnNode.ParentTile;
                    }
                    return ReturnList; // Juhu, Pfad gefunden 
                }

                Tile LowestTile = KnotenmitgeringstemF(); // Leifert den Knoten mit den geringsten Kosten aus der offenen Liste 
                lstOpen.Remove(LowestTile); // Verschliebt den Knoten mit den niedrigsten Kosten ...
                lstClosed.Add(LowestTile); // ... in die "Bereits Untersucht" Liste 

                for (int i = 1; i <= 8; i++) // Alle umliegenden Felder durchsuchen -> außer die diagonalen
                {
                    Tile TempTile;
                    switch (i)
                    {
                        case 1:
                            //TempTile = TheMap.GetTileByPosInFieldSize(new Vector2((int)LowestTile.TileX + 1, (int)LowestTile.TileY + 1));
                            //NeuesTitle(TempTile, 14, LowestTile, TheMap);
                            break;
                        case 2:
                            TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX + 1, (int)LowestTile.TileY)));
                            NeuesTitle(TempTile, 10, LowestTile, TheMap);
                            break;
                        case 3:
                            //TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX + 1, (int)LowestTile.TileY - 1)));
                            //NeuesTitle(TempTile, 14, LowestTile, TheMap);
                            break;
                        case 4:
                            TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX, (int)LowestTile.TileY + 1)));
                            NeuesTitle(TempTile, 10, LowestTile, TheMap);
                            break;
                        case 5:
                            //TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX - 1, (int)LowestTile.TileY + 1)));
                            //NeuesTitle(TempTile, 14, LowestTile, TheMap);
                            break;
                        case 6:
                            TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX - 1, (int)LowestTile.TileY)));
                            NeuesTitle(TempTile, 10, LowestTile, TheMap);
                            break;
                        case 7:
                            //TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX - 1, (int)LowestTile.TileY - 1)));
                            //NeuesTitle(TempTile, 14, LowestTile, TheMap);
                            break;
                        case 8:
                            TempTile = TheMap.GetTileByPosInFieldSize((new Vector2((int)LowestTile.TileX, (int)LowestTile.TileY - 1)));
                            NeuesTitle(TempTile, 10, LowestTile, TheMap);
                            break;
                    }
                    if (lstClosed.Contains(EndTile)) // Wenn der Endknoten gefunden wurde 
                    {
                        List<Tile> ReturnList = new List<Tile>(); // Neue Liste 
                        Tile TempReturnNode = lstClosed[lstClosed.Count - 1]; // Den Endknoten nehmen ... 
                        while (TempReturnNode != StartTile) //  ... Und solang zurückverfolgen bis der Start gefunden wurde 
                        {
                            ReturnList.Add(TempReturnNode);
                            TempReturnNode = TempReturnNode.ParentTile;
                        }
                        return ReturnList; // Ein Pfad gefunden 
                    }
                }


            }

            return new List<Tile>();
        }


        private int NeuesTitle(Tile TheTile, int addG, Tile LowestTile, TileMap map) // Berechnet den "neuen" Tile 
        {
            //try
            //{
            //    //TheTile.TileEnterable = map.TileBetretbar(TheTile.TileX, TheTile.TileY); // HINZUGEFÜGT !!!
            //}
            //catch
            //{
            //    return 0;
            //}
            // changed 12.03.2012 Domi

            if (TheTile == null) // Wenn er null ist (kann beim Rand vorkommen) breche ab 
                return 0;
            if (TheTile.TileEnterable == true && lstClosed.Contains(TheTile) == false)
            {
                if (!lstOpen.Contains(TheTile)) // Wenns noch nicht in der "zu untersuchen" Liste ist 
                {
                    TheTile.ParentTile = LowestTile;
                    TheTile.GCount = LowestTile.GCount + addG;
                    TheTile.HCount = GetH(TheTile, tEndTile, map);
                    TheTile.FCount = TheTile.GCount + TheTile.HCount;
                    lstOpen.Add(TheTile);
                }
                else // Wenn der Knoten bereits in der "zu untersuchen" Liste ist 
                {
                    Tile TempParent = TheTile.ParentTile;
                    if (TempParent.GCount >= LowestTile.GCount)
                    {
                        TheTile.ParentTile = LowestTile;
                        TheTile.GCount = LowestTile.GCount + addG;
                        TheTile.HCount = GetH(TheTile, tEndTile, map);
                        TheTile.FCount = TheTile.GCount + TheTile.HCount;
                    }
                }
            }
            return 1;
        }


        private Tile KnotenmitgeringstemF() // Liefert den Knoten mit den geringsten Kosten aus lstOpen 
        {
            int LowestF = 10000000; // Eine hohe Zahl 
            Tile LowestTile = new Tile();

            foreach (Tile ITEM in lstOpen) //foreach : liste durchgehen !
            {
                if (ITEM.HCount < LowestF) // Wenn Kosten kleiner als bisher kleinsten Kosten 
                {
                    LowestF = ITEM.HCount;
                    LowestTile = ITEM;
                }
            }

            return LowestTile; // Tile mit niedrigesten Kosten zurückliefern 
        }
    }
}
